// src/backtesting/simulated-contract-account.service.ts
import { formatToUTC8 } from '../utils/time.utils';

/**
 * 仓位方向
 */
export enum PositionSide {
  LONG = 'long',
  SHORT = 'short',
  NONE = 'none'
}

/**
 * 合约仓位信息
 */
export interface ContractPosition {
  side: PositionSide;      // 仓位方向
  size: number;            // 仓位数量 (单位: 币)
  entryPrice: number;      // 开仓均价
  margin: number;          // 占用的保证金
  liquidationPrice: number; // 预估强平价
}

/**
 * 模拟合约账户服务
 *
 * 负责模拟合约交易的核心逻辑，包括：
 * - 杠杆、保证金计算
 * - 开多、开空、平多、平空
 * - 未实现盈亏计算
 * - 强平价格计算与执行
 * - 风险指标（如最大回撤）跟踪
 */
export class SimulatedContractAccount {
  private readonly initialBalance: number;
  private balance: number; // 账户余额
  private readonly feeRate: number;
  private readonly leverage: number;

  public position: ContractPosition | null = null; // 当前持有的仓位
  public trades: any[] = [];
  private peakBalance: number;
  private maxDrawdown: number = 0;

  constructor(initialBalance: number, feeRate: number, leverage: number) {
    this.initialBalance = initialBalance;
    this.balance = initialBalance;
    this.feeRate = feeRate;
    this.leverage = leverage;
    this.peakBalance = initialBalance;
    console.log(`\n--- Contract Account Initialized ---`);
    console.log(`Leverage: ${leverage}x`);
    console.log(`------------------------------------`);
  }

  /**
   * 开多仓
   */
  public openLong(price: number, timestamp: number): void {
    // 如果当前有空头仓位，这是一个反转信号，先平掉空仓
    if (this.position && this.position.side === PositionSide.SHORT) {
      this.closeShort(price, timestamp);
    }

    // 如果当前已经持有多仓，则不重复开仓
    if (this.position && this.position.side === PositionSide.LONG) {
      return;
    }

    const margin = this.balance; // 使用全部余额作为保证金
    if (margin <= 0) return; // 余额不足，无法开仓

    const positionValue = margin * this.leverage;
    const size = positionValue / price;
    const fee = positionValue * this.feeRate;

    this.balance -= fee;

    this.position = {
      side: PositionSide.LONG,
      size,
      entryPrice: price,
      margin,
      liquidationPrice: this.calculateLiquidationPrice(PositionSide.LONG, price, margin, size)
    };

    this.logTrade('OPEN_LONG', price, size, timestamp);
  }

  /**
   * 平多仓
   */
  public closeLong(price: number, timestamp: number): void {
    if (!this.position || this.position.side !== PositionSide.LONG) {
      return;
    }

    const pnl = (price - this.position.entryPrice) * this.position.size;
    const positionValue = this.position.size * price;
    const fee = positionValue * this.feeRate;

    this.balance += pnl - fee;

    this.logTrade('CLOSE_LONG', price, this.position.size, timestamp, pnl);
    this.position = null;
  }

  /**
   * 开空仓
   */
  public openShort(price: number, timestamp: number): void {
    // 如果当前有多头仓位，这是一个反转信号，先平掉多仓
    if (this.position && this.position.side === PositionSide.LONG) {
      this.closeLong(price, timestamp);
    }

    // 如果当前已经持有空仓，则不重复开仓
    if (this.position && this.position.side === PositionSide.SHORT) {
      return;
    }

    const margin = this.balance;
    if (margin <= 0) return; // 余额不足，无法开仓

    const positionValue = margin * this.leverage;
    const size = positionValue / price;
    const fee = positionValue * this.feeRate;

    this.balance -= fee;

    this.position = {
      side: PositionSide.SHORT,
      size,
      entryPrice: price,
      margin,
      liquidationPrice: this.calculateLiquidationPrice(PositionSide.SHORT, price, margin, size)
    };

    this.logTrade('OPEN_SHORT', price, size, timestamp);
  }

  /**
   * 平空仓
   */
  public closeShort(price: number, timestamp: number): void {
    if (!this.position || this.position.side !== PositionSide.SHORT) {
      return;
    }

    const pnl = (this.position.entryPrice - price) * this.position.size;
    const positionValue = this.position.size * price;
    const fee = positionValue * this.feeRate;

    this.balance += pnl - fee;

    this.logTrade('CLOSE_SHORT', price, this.position.size, timestamp, pnl);
    this.position = null;
  }

  /**
   * 在每个K线周期结束后，更新账户状态
   */
  public updatePnlAndDrawdown(kline: { high: number, low: number, close: number, timestamp: number }): void {
    // 检查是否触发强平
    if (this.position) {
      let liquidated = false;
      if (this.position.side === PositionSide.LONG && kline.low <= this.position.liquidationPrice) {
        this.liquidate(this.position.liquidationPrice, kline.timestamp as number);
        liquidated = true;
      } else if (this.position.side === PositionSide.SHORT && kline.high >= this.position.liquidationPrice) {
        this.liquidate(this.position.liquidationPrice, kline.timestamp as number);
        liquidated = true;
      }

      // 如果未被强平，则更新浮动盈亏和最大回撤
      if (!liquidated) {
        const currentValue = this.getTotalValue(kline.close);
        if (currentValue > this.peakBalance) {
          this.peakBalance = currentValue;
        }
        const drawdown = (this.peakBalance - currentValue) / this.peakBalance;
        if (drawdown > this.maxDrawdown) {
          this.maxDrawdown = drawdown;
        }
      }
    } else {
      // 没有仓位时，账户余额就是总价值
      if (this.balance > this.peakBalance) {
        this.peakBalance = this.balance;
      }
    }
  }

  /**
   * 强平
   */
  private liquidate(price: number, timestamp: number): void {
    if (!this.position) return;

    const pnl = (this.position.side === PositionSide.LONG)
      ? (price - this.position.entryPrice) * this.position.size
      : (this.position.entryPrice - price) * this.position.size;

    const side = this.position.side.toUpperCase();
    const amount = this.position.size;
    const preLiquidationBalance = this.balance;

    this.logTrade('LIQUIDATION', price, amount, timestamp, pnl, side, preLiquidationBalance);

    this.balance = 0; // 全仓模式下，强平后余额归零
    this.position = null;
  }

  /**
   * 计算预估强平价 (简化模型)
   * 这是一个简化的全仓保证金模型，未考虑维持保证金率等复杂因素
   */
  private calculateLiquidationPrice(side: PositionSide, entryPrice: number, margin: number, size: number): number {
    if (side === PositionSide.LONG) {
      return entryPrice - (margin / size);
    } else { // SHORT
      return entryPrice + (margin / size);
    }
  }

  /**
   * 获取当前账户的总资产价值
   */
  public getTotalValue(currentPrice: number): number {
    if (!this.position) {
      return this.balance;
    }
    const unrealizedPnl = (this.position.side === PositionSide.LONG)
      ? (currentPrice - this.position.entryPrice) * this.position.size
      : (this.position.entryPrice - currentPrice) * this.position.size;

    return this.balance + unrealizedPnl;
  }

  /**
   * 记录交易
   */
  private logTrade(side: string, price: number, amount: number, timestamp: number, pnl: number = 0, positionSide?: string, preLiquidationBalance?: number): void {
    const tradeInfo = {
      side,
      price,
      amount,
      timestamp,
      pnl,
      balance: this.balance,
    };
    this.trades.push(tradeInfo);

    const pnlStr = pnl ? ` | PnL: ${pnl.toFixed(2)}` : '';
    if (side === 'LIQUIDATION') {
      console.error(`[LIQUIDATION] ${formatToUTC8(new Date(timestamp))} | Side: ${positionSide} | Price: ${price.toFixed(4)} | Amount: ${amount.toFixed(6)}${pnlStr} | Pre-Balance: ${preLiquidationBalance?.toFixed(2)} -> Post-Balance: 0.00`);
    } else {
      console.log(`[TRADE] ${formatToUTC8(new Date(timestamp))} | ${side.padEnd(12)} | Price: ${price.toFixed(4)} | Amount: ${amount.toFixed(6)}${pnlStr} | Balance: ${this.balance.toFixed(2)}`);
    }
  }

  /**
   * 打印最终回测结果
   */
  public printResults(finalPrice: number): void {
    const finalValue = this.getTotalValue(finalPrice);
    const profit = finalValue - this.initialBalance;
    const profitRate = (profit / this.initialBalance) * 100;

    // 为合约模式计算“买入并持有”回报率，作为基准
    const buyAndHoldValue = (this.initialBalance / this.trades[0]?.price) * finalPrice || this.initialBalance;
    const buyAndHoldReturn = ((buyAndHoldValue - this.initialBalance) / this.initialBalance) * 100;

    console.log("\n--- Contract Backtest Results ---");
    console.log(`Initial Balance  : ${this.initialBalance.toFixed(2)} USDT`);
    console.log(`Final Balance    : ${finalValue.toFixed(2)} USDT`);
    console.log(`Total Profit     : ${profit.toFixed(2)} USDT`);
    console.log(`Return Rate      : ${profitRate.toFixed(2)}%`);
    console.log(`Max Drawdown     : ${(this.maxDrawdown * 100).toFixed(2)}%`);
    console.log(`Buy & Hold Return: ${buyAndHoldReturn.toFixed(2)}%`);
    console.log(`Total Trades     : ${this.trades.length}`);
    console.log("-----------------------------------\n");
  }
}